{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Introduction to Natural Language Processing with fastText\n",
    "\n",
    "Natural Language Processing (NLP) is one of the hottest areas in machine learning. Its global purpose is to understand language the way humans do. [NLP subareas](https://en.wikipedia.org/wiki/Natural_language_processing) include machine translation, text classification, speech recognition, sentiment analysis, question answering, text-to-speech, etc. \n",
    "\n",
    "As in most areas of Machine Learning, NLP accuracy has improved considerably thanks to deep learning. Just to highlight the most recent and impressive achievement, in October 2016 [Microsoft Research reached human parity in speech recognition](http://blogs.microsoft.com/next/2016/10/18/historic-achievement-microsoft-researchers-reach-human-parity-conversational-speech-recognition/). For that milestone, they used a combination of Convolutional Neural Networks and LSTM networks. \n",
    "\n",
    "However, not all machine learning is deep learning, and in this notebook I would like to highlight a great example. In the summer of 2016, two interesting NLP papers were published by Facebook Research, [Bojanowski et al., 2016](https://arxiv.org/abs/1607.04606) and [Joulin et al., 2016](https://arxiv.org/abs/1607.01759). The first one proposed a new method for word embedding and the second one a method for text classification. The authors also opensourced a C++ library with the implementation of these methods, [fastText](https://github.com/facebookresearch/fastText), that rapidly attracted a lot of interest.  \n",
    "\n",
    "The reason for this interest is that fastText obtains an accuracy in text classification almost as good as the state of the art in deep learning, but it is several orders of magnitude faster. In their paper, the authors compare the accuracy and computation time of several datasets with deep nets. As an example, in the Amazon Polarity dataset, fastText achieves an accuracy of 94.6% in 10s. In the same dataset, the crepe CNN model of [Zhang and LeCun, 2016](https://arxiv.org/abs/1509.01626) achieves 94.5% in 5 days, while the Very Deep CNN model of [Conneau et al., 2016](https://arxiv.org/abs/1606.01781) achieves 95.7% in 7h. The comparison is not even fair, because while fastText's time is computed with CPUs, the CNN models are computed using Tesla K40 GPUs. \n",
    "\n",
    "In this notebook we will discuss how to easily implement several projects using a python wrapper of fastText, [fastText.py](https://github.com/salestock/fastText.py)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Populating the interactive namespace from numpy and matplotlib\n",
      "3.5.2 |Anaconda custom (64-bit)| (default, Jul  2 2016, 17:53:06) \n",
      "[GCC 4.4.7 20120313 (Red Hat 4.4.7-1)]\n"
     ]
    }
   ],
   "source": [
    "#Load all libraries\n",
    "import os,sys  \n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "import fasttext\n",
    "try:\n",
    "    from urllib.request import urlopen     # For Python 3.0 and later\n",
    "except ImportError:\n",
    "    from urllib2 import urlopen     # Fall back to Python 2's urllib2\n",
    "try:\n",
    "    from html import unescape  # python 3.4+\n",
    "except ImportError:\n",
    "    try:\n",
    "        from html.parser import HTMLParser  # python 3.x (<3.4)\n",
    "    except ImportError:\n",
    "        from HTMLParser import HTMLParser  # python 2.x\n",
    "    unescape = HTMLParser().unescape\n",
    "from sklearn.manifold import TSNE\n",
    "%pylab inline\n",
    "\n",
    "print(sys.version)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Text classification\n",
    "The first task will be to perform text classification dataset DBPedia, which can be accessed [here](https://drive.google.com/drive/folders/0Bz8a_Dbh9Qhbfll6bVpmNUtUcFdjYmF2SEpmZUZUcVNiMUw1TWN6RDV3a0JHT3kxLVhVR2M). The dataset consists of text descriptions of 14 different classes. The training set contains 560,000 reviews and the test contains 70,000. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>class</th>\n",
       "      <th>name</th>\n",
       "      <th>description</th>\n",
       "      <th>class_name</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1</td>\n",
       "      <td>E. D. Abbott Ltd</td>\n",
       "      <td>Abbott of Farnham E D Abbott Limited was a Br...</td>\n",
       "      <td>Company</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>1</td>\n",
       "      <td>Schwan-Stabilo</td>\n",
       "      <td>Schwan-STABILO is a German maker of pens for ...</td>\n",
       "      <td>Company</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>1</td>\n",
       "      <td>Q-workshop</td>\n",
       "      <td>Q-workshop is a Polish company located in Poz...</td>\n",
       "      <td>Company</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>1</td>\n",
       "      <td>Marvell Software Solutions Israel</td>\n",
       "      <td>Marvell Software Solutions Israel known as RA...</td>\n",
       "      <td>Company</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>1</td>\n",
       "      <td>Bergan Mercy Medical Center</td>\n",
       "      <td>Bergan Mercy Medical Center is a hospital loc...</td>\n",
       "      <td>Company</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   class                               name  \\\n",
       "0      1                   E. D. Abbott Ltd   \n",
       "1      1                     Schwan-Stabilo   \n",
       "2      1                         Q-workshop   \n",
       "3      1  Marvell Software Solutions Israel   \n",
       "4      1        Bergan Mercy Medical Center   \n",
       "\n",
       "                                         description class_name  \n",
       "0   Abbott of Farnham E D Abbott Limited was a Br...    Company  \n",
       "1   Schwan-STABILO is a German maker of pens for ...    Company  \n",
       "2   Q-workshop is a Polish company located in Poz...    Company  \n",
       "3   Marvell Software Solutions Israel known as RA...    Company  \n",
       "4   Bergan Mercy Medical Center is a hospital loc...    Company  "
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Set dataset path\n",
    "\n",
    "data_path = ''\n",
    "\n",
    "#Load train set\n",
    "train_file = data_path + 'dbpedia_train.csv'\n",
    "df = pd.read_csv(train_file, header=None, names=['class','name','description'])\n",
    "\n",
    "#Load test set\n",
    "test_file = data_path + 'dbpedia_test.csv'\n",
    "df_test = pd.read_csv(test_file, header=None, names=['class','name','description'])\n",
    "\n",
    "#Mapping from class number to class name\n",
    "class_dict={\n",
    "1:'Company',\n",
    "2:'EducationalInstitution',\n",
    "3:'Artist',\n",
    "4:'Athlete',\n",
    "5:'OfficeHolder',\n",
    "6:'MeanOfTransportation',\n",
    "7:'Building',\n",
    "8:'NaturalPlace',\n",
    "9:'Village',\n",
    "10:'Animal',\n",
    "11:'Plant',\n",
    "12:'Album',\n",
    "13:'Film',\n",
    "14:'WrittenWork'\n",
    "}\n",
    "df['class_name'] = df['class'].map(class_dict)\n",
    "df.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr>\n",
       "      <th>class</th>\n",
       "      <th colspan=\"4\" halign=\"left\">1</th>\n",
       "      <th colspan=\"4\" halign=\"left\">2</th>\n",
       "      <th colspan=\"2\" halign=\"left\">3</th>\n",
       "      <th>...</th>\n",
       "      <th colspan=\"2\" halign=\"left\">12</th>\n",
       "      <th colspan=\"4\" halign=\"left\">13</th>\n",
       "      <th colspan=\"4\" halign=\"left\">14</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <th>count</th>\n",
       "      <th>unique</th>\n",
       "      <th>top</th>\n",
       "      <th>freq</th>\n",
       "      <th>count</th>\n",
       "      <th>unique</th>\n",
       "      <th>top</th>\n",
       "      <th>freq</th>\n",
       "      <th>count</th>\n",
       "      <th>unique</th>\n",
       "      <th>...</th>\n",
       "      <th>top</th>\n",
       "      <th>freq</th>\n",
       "      <th>count</th>\n",
       "      <th>unique</th>\n",
       "      <th>top</th>\n",
       "      <th>freq</th>\n",
       "      <th>count</th>\n",
       "      <th>unique</th>\n",
       "      <th>top</th>\n",
       "      <th>freq</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>class_name</th>\n",
       "      <td>40000</td>\n",
       "      <td>1</td>\n",
       "      <td>Company</td>\n",
       "      <td>40000</td>\n",
       "      <td>40000</td>\n",
       "      <td>1</td>\n",
       "      <td>EducationalInstitution</td>\n",
       "      <td>40000</td>\n",
       "      <td>40000</td>\n",
       "      <td>1</td>\n",
       "      <td>...</td>\n",
       "      <td>Album</td>\n",
       "      <td>40000</td>\n",
       "      <td>40000</td>\n",
       "      <td>1</td>\n",
       "      <td>Film</td>\n",
       "      <td>40000</td>\n",
       "      <td>40000</td>\n",
       "      <td>1</td>\n",
       "      <td>WrittenWork</td>\n",
       "      <td>40000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>description</th>\n",
       "      <td>40000</td>\n",
       "      <td>39996</td>\n",
       "      <td>MegaPath Corporation—headquartered in Pleasan...</td>\n",
       "      <td>2</td>\n",
       "      <td>40000</td>\n",
       "      <td>39992</td>\n",
       "      <td>Akuressa Training Center of National Youth Se...</td>\n",
       "      <td>2</td>\n",
       "      <td>40000</td>\n",
       "      <td>40000</td>\n",
       "      <td>...</td>\n",
       "      <td>Before Smile Empty Soul became Smile Empty So...</td>\n",
       "      <td>2</td>\n",
       "      <td>40000</td>\n",
       "      <td>40000</td>\n",
       "      <td>Koryo Celadon is a 1979 American short docume...</td>\n",
       "      <td>1</td>\n",
       "      <td>40000</td>\n",
       "      <td>39984</td>\n",
       "      <td>Tom Clancy's Net Force Explorers or Net Force...</td>\n",
       "      <td>15</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>name</th>\n",
       "      <td>40000</td>\n",
       "      <td>40000</td>\n",
       "      <td>Tsokkos</td>\n",
       "      <td>1</td>\n",
       "      <td>40000</td>\n",
       "      <td>40000</td>\n",
       "      <td>Christ's School</td>\n",
       "      <td>1</td>\n",
       "      <td>40000</td>\n",
       "      <td>40000</td>\n",
       "      <td>...</td>\n",
       "      <td>Indispensable: The Best of Michael Franks</td>\n",
       "      <td>1</td>\n",
       "      <td>40000</td>\n",
       "      <td>40000</td>\n",
       "      <td>Netaji Subhas Chandra Bose: The Forgotten Hero</td>\n",
       "      <td>1</td>\n",
       "      <td>40000</td>\n",
       "      <td>40000</td>\n",
       "      <td>A Star Called Henry</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>3 rows × 56 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "class           1                                                             \\\n",
       "             count unique                                                top   \n",
       "class_name   40000      1                                            Company   \n",
       "description  40000  39996   MegaPath Corporation—headquartered in Pleasan...   \n",
       "name         40000  40000                                            Tsokkos   \n",
       "\n",
       "class                  2          \\\n",
       "              freq  count unique   \n",
       "class_name   40000  40000      1   \n",
       "description      2  40000  39992   \n",
       "name             1  40000  40000   \n",
       "\n",
       "class                                                                     3   \\\n",
       "                                                           top   freq  count   \n",
       "class_name                              EducationalInstitution  40000  40000   \n",
       "description   Akuressa Training Center of National Youth Se...      2  40000   \n",
       "name                                           Christ's School      1  40000   \n",
       "\n",
       "class               ...                                                   12  \\\n",
       "            unique  ...                                                  top   \n",
       "class_name       1  ...                                                Album   \n",
       "description  40000  ...     Before Smile Empty Soul became Smile Empty So...   \n",
       "name         40000  ...            Indispensable: The Best of Michael Franks   \n",
       "\n",
       "class                  13         \\\n",
       "              freq  count unique   \n",
       "class_name   40000  40000      1   \n",
       "description      2  40000  40000   \n",
       "name             1  40000  40000   \n",
       "\n",
       "class                                                                     14  \\\n",
       "                                                           top   freq  count   \n",
       "class_name                                                Film  40000  40000   \n",
       "description   Koryo Celadon is a 1979 American short docume...      1  40000   \n",
       "name            Netaji Subhas Chandra Bose: The Forgotten Hero      1  40000   \n",
       "\n",
       "class                                                                         \n",
       "            unique                                                top   freq  \n",
       "class_name       1                                        WrittenWork  40000  \n",
       "description  39984   Tom Clancy's Net Force Explorers or Net Force...     15  \n",
       "name         40000                                A Star Called Henry      1  \n",
       "\n",
       "[3 rows x 56 columns]"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#df.describe().transpose()\n",
    "desc = df.groupby('class')\n",
    "desc.describe().transpose()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The next step is to treat the data. As of today, the python wrapper of fastText doesn't allow dataframes or iterators as inputs to their functions (however, they are [working on it](https://github.com/salestock/fastText.py/issues/78)). We have to create an intermediate file. This intermediate file doesn't have commas, non-ascii characters and everything is lowercase. The changes are based on [this script](https://github.com/facebookresearch/fastText/blob/a88344f6de234bdefd003e9e55512eceedde3ec0/classification-example.sh#L17)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def clean_dataset(dataframe, shuffle=False, encode_ascii=False, clean_strings = False, label_prefix='__label__'):\n",
    "    # Transform train file\n",
    "    df = dataframe[['name','description']].apply(lambda x: x.str.replace(',',' '))\n",
    "    df['class'] = label_prefix + dataframe['class'].astype(str) + ' '\n",
    "    if clean_strings:\n",
    "        df[['name','description']] = df[['name','description']].apply(lambda x: x.str.replace('\"',''))\n",
    "        df[['name','description']] = df[['name','description']].apply(lambda x: x.str.replace('\\'',' \\' '))\n",
    "        df[['name','description']] = df[['name','description']].apply(lambda x: x.str.replace('.',' . '))\n",
    "        df[['name','description']] = df[['name','description']].apply(lambda x: x.str.replace('(',' ( '))\n",
    "        df[['name','description']] = df[['name','description']].apply(lambda x: x.str.replace(')',' ) '))\n",
    "        df[['name','description']] = df[['name','description']].apply(lambda x: x.str.replace('!',' ! '))\n",
    "        df[['name','description']] = df[['name','description']].apply(lambda x: x.str.replace('?',' ? '))\n",
    "        df[['name','description']] = df[['name','description']].apply(lambda x: x.str.replace(':',' '))\n",
    "        df[['name','description']] = df[['name','description']].apply(lambda x: x.str.replace(';',' '))\n",
    "        df[['name','description']] = df[['name','description']].apply(lambda x: x.str.lower())\n",
    "    if shuffle:\n",
    "        df.sample(frac=1).reset_index(drop=True)\n",
    "    if encode_ascii :\n",
    "        df[['name','description']] = df[['name','description']].apply(lambda x: x.str.normalize('NFKD').str.encode('ascii','ignore').str.decode('utf-8'))\n",
    "    df['name'] = ' ' + df['name'] + ' '\n",
    "    df['description'] = ' ' + df['description'] + ' '\n",
    "    return df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 9.75 s, sys: 744 ms, total: 10.5 s\n",
      "Wall time: 10.7 s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "# Transform datasets\n",
    "df_train_clean = clean_dataset(df, True, False)\n",
    "df_test_clean = clean_dataset(df_test, False, False)\n",
    "\n",
    "# Write files to disk\n",
    "train_file_clean = data_path + 'dbpedia.train'\n",
    "df_train_clean.to_csv(train_file_clean, header=None, index=False, columns=['class','name','description'] )\n",
    "\n",
    "test_file_clean = data_path + 'dbpedia.test'\n",
    "df_test_clean.to_csv(test_file_clean, header=None, index=False, columns=['class','name','description'] )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once the dataset is cleaned, the next step is to train the classifier. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1min 20s, sys: 1.3 s, total: 1min 21s\n",
      "Wall time: 11.8 s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "# Train a classifier\n",
    "output_file = data_path + 'dp_model'\n",
    "classifier = fasttext.supervised(train_file_clean, output_file, label_prefix='__label__')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once the model is trained, we can test its accuracy. We can obtain the [precision and recall](https://en.wikipedia.org/wiki/Precision_and_recall) of the model. High precision means that an algorithm returned substantially more relevant results than irrelevant ones, while high recall means that an algorithm returned most of the relevant results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "P@1: 0.9797857142857143\n",
      "R@1: 0.9797857142857143\n",
      "Number of examples: 70000\n",
      "CPU times: user 564 ms, sys: 0 ns, total: 564 ms\n",
      "Wall time: 562 ms\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "# Evaluate classifier\n",
    "result = classifier.test(test_file_clean)\n",
    "print('P@1:', result.precision)\n",
    "print('R@1:', result.recall)\n",
    "print ('Number of examples:', result.nexamples)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The next step is to check how the model works with real sentences."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Sentence:  Picasso was a famous painter born in Malaga, Spain. He revolutionized the art in the 20th century.\n",
      "Label: 3; label name: Artist\n",
      "Sentence:  One of my favourite tennis players in the world is Rafa Nadal.\n",
      "Label: 4; label name: Athlete; certainty: 0.904297\n",
      "Sentence:  Say what one more time, I dare you, I double-dare you motherfucker!\n",
      "Label: 12; label name: Album; certainty: 0.287109\n",
      "Label: 14; label name: WrittenWork; certainty: 0.246094\n",
      "Label: 1; label name: Company; certainty: 0.240234\n"
     ]
    }
   ],
   "source": [
    "sentence1 = ['Picasso was a famous painter born in Malaga, Spain. He revolutionized the art in the 20th century.']\n",
    "labels1 = classifier.predict(sentence1)\n",
    "class1 = int(labels1[0][0])\n",
    "print(\"Sentence: \", sentence1[0])\n",
    "print(\"Label: %d; label name: %s\" %(class1, class_dict[class1]))\n",
    "\n",
    "sentence2 = ['One of my favourite tennis players in the world is Rafa Nadal.']\n",
    "labels2 = classifier.predict_proba(sentence2)\n",
    "class2, prob2 = labels2[0][0] # it returns class2 as string\n",
    "print(\"Sentence: \", sentence2[0])\n",
    "print(\"Label: %s; label name: %s; certainty: %f\" %(class2, class_dict[int(class2)], prob2))\n",
    "\n",
    "sentence3 = ['Say what one more time, I dare you, I double-dare you motherfucker!']\n",
    "number_responses = 3\n",
    "labels3 = classifier.predict_proba(sentence3, k=number_responses)\n",
    "print(\"Sentence: \", sentence3[0])\n",
    "for l in range(number_responses):\n",
    "    class3, prob3 = labels3[0][l]\n",
    "    print(\"Label: %s; label name: %s; certainty: %f\" %(class3, class_dict[int(class3)], prob3))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The model predicts sentence 1 as `Artist`, which is correct. Sentence 2 is also predicted correctly. This time we used the function `predict_proba` that returns the certainty of the prediction as a probability. Finally, sentence 3 was not correctly classified. The correct label would be `Film`, since the sentence is from a famous scene of a very good film. If by any chance, you don't know [what I'm talking about](https://www.youtube.com/watch?v=xwT60UbOZnI), well, please put your priorities in order. Stop reading this notebook, go to see Pulp Fiction, and then come back to keep learning NLP :-)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## Sentiment Analysis\n",
    "Sentiment analysis is one of the most important use cases in text classification. The objective is to classify a piece of text into positive, negative and, in some cases, neutral. This is extensively used by brands to understand the perception their customers have on their products. Sentiment analysis can influence marketing campaigns, generate leads, plan product development or improve customer service.  \n",
    "\n",
    "In our notebook, we will use the Amazon polarity dataset, which contains 3.6 million reviews in the train set and 400,000 reviews in the test set. The reviews are positive or negative. The dataset can be found [here](https://drive.google.com/drive/folders/0Bz8a_Dbh9Qhbfll6bVpmNUtUcFdjYmF2SEpmZUZUcVNiMUw1TWN6RDV3a0JHT3kxLVhVR2M). \n",
    "\n",
    "The first step is to prepare the dataset for the algorithm format."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1min 36s, sys: 7.23 s, total: 1min 43s\n",
      "Wall time: 3min 19s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "#Load train set\n",
    "train_file = data_path + 'amazon_review_polarity_train.csv'\n",
    "df_sentiment_train = pd.read_csv(train_file, header=None, names=['class','name','description'])\n",
    "\n",
    "#Load test set\n",
    "test_file = data_path + 'amazon_review_polarity_test.csv'\n",
    "df_sentiment_test = pd.read_csv(test_file, header=None, names=['class','name','description'])\n",
    "\n",
    "# Transform datasets\n",
    "df_train_clean = clean_dataset(df_sentiment_train, True, False)\n",
    "df_test_clean = clean_dataset(df_sentiment_test, False, False)\n",
    "\n",
    "# Write files to disk\n",
    "train_file_clean = data_path + 'amazon.train'\n",
    "df_train_clean.to_csv(train_file_clean, header=None, index=False, columns=['class','name','description'] )\n",
    "\n",
    "test_file_clean = data_path + 'amazon.test'\n",
    "df_test_clean.to_csv(test_file_clean, header=None, index=False, columns=['class','name','description'] )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once we have the data, let's train the classifier. This time instead of the default parameters we are going to use those used in the [fastText paper](https://arxiv.org/abs/1607.01759)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 8min, sys: 15.1 s, total: 8min 15s\n",
      "Wall time: 1min 10s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "# Parameters\n",
    "dim=10\n",
    "lr=0.1\n",
    "epoch=5\n",
    "min_count=1\n",
    "word_ngrams=2\n",
    "bucket=10000000\n",
    "thread=12\n",
    "label_prefix='__label__'\n",
    "\n",
    "# Train a classifier\n",
    "output_file = data_path + 'amazon_model'\n",
    "classifier = fasttext.supervised(train_file_clean, output_file, dim=dim, lr=lr, epoch=epoch,\n",
    "                                 min_count=min_count, word_ngrams=word_ngrams, bucket=bucket,\n",
    "                                 thread=thread, label_prefix=label_prefix)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's evaluate the results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "P@1: 0.9418725\n",
      "R@1: 0.9418725\n",
      "Number of examples: 400000\n",
      "CPU times: user 5.27 s, sys: 80 ms, total: 5.35 s\n",
      "Wall time: 5.34 s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "# Evaluate classifier\n",
    "result = classifier.test(test_file_clean)\n",
    "print('P@1:', result.precision)\n",
    "print('R@1:', result.recall)\n",
    "print ('Number of examples:', result.nexamples)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, let's evaluate some sentences"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Sentence:  The product design is nice but it's working as expected\n",
      "Label: 2; label name: Positive; certainty: 0.992187\n",
      "Sentence:  I bought the product a month ago and it was working correctly. But now is not working great\n",
      "Label: 1; label name: Negative; certainty: 0.917969\n"
     ]
    }
   ],
   "source": [
    "class_dict={\n",
    "    1:\"Negative\",\n",
    "    2:\"Positive\"\n",
    "}\n",
    "\n",
    "sentence1 = [\"The product design is nice but it's working as expected\"]\n",
    "labels1 = classifier.predict_proba(sentence1)\n",
    "class1, prob1 = labels1[0][0] # it returns class as string\n",
    "print(\"Sentence: \", sentence1[0])\n",
    "print(\"Label: %s; label name: %s; certainty: %f\" %(class1, class_dict[int(class1)], prob1))\n",
    "\n",
    "sentence2 = [\"I bought the product a month ago and it was working correctly. But now is not working great\"]\n",
    "labels2 = classifier.predict_proba(sentence2)\n",
    "class2, prob2 = labels2[0][0] # it returns class as string\n",
    "print(\"Sentence: \", sentence2[0])\n",
    "print(\"Label: %s; label name: %s; certainty: %f\" %(class2, class_dict[int(class2)], prob2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can use a tweet as the text input. Twitter has a [REST API](https://dev.twitter.com/rest/public) that allows you to interact with all their data. However, everything from that API requires authentication (an example can be found in this [Twitter bot](https://github.com/miguelgfierro/twitter_bot) I made). Instead, we can parse the title of the tweet url, which contains the text of the tweet."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Miguel G-Fierro on Twitter: &quot;Today at @Microsoft we had a demo of the hololens. It is one of the most spectacular things I&#39;ve seen in my life&quot;\n",
      "Miguel G-Fierro on Twitter: \"Today at @Microsoft we had a demo of the hololens. It is one of the most spectacular things I've seen in my life\"\n",
      "Label: 2; label name: Positive; certainty: 0.998047\n"
     ]
    }
   ],
   "source": [
    "# Get twitter data\n",
    "url = \"https://twitter.com/miguelgfierro/status/805827479139192832\"\n",
    "response = urlopen(url).read()\n",
    "title = str(response).split('<title>')[1].split('</title>')[0]\n",
    "print(title)\n",
    "\n",
    "# Format tweet\n",
    "tweet = unescape(title)\n",
    "print(tweet)\n",
    "\n",
    "# Classify tweet\n",
    "label_tweet = classifier.predict_proba([tweet])\n",
    "class_tweet, prob_tweet = label_tweet[0][0]\n",
    "print(\"Label: %s; label name: %s; certainty: %f\" %(class_tweet, class_dict[int(class_tweet)], prob_tweet))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Word representations\n",
    "We can also use fastText to learn word vectors. This is a way to represent words in a multidimensional space, where each word is represented as a vector. Similar words will be clustered in a specific region of this multidimensional space. \n",
    "\n",
    "As an example we are going to download the English Wikipedia 9 dataset: [enwik9](http://mattmahoney.net/dc/enwik9.zip). The data is UTF-8 encoded XML consisting primarily of English text. enwik9 contains 243,426 article titles, of which 85,560 are #REDIRECT to fix broken links, and the rest are regular articles.  \n",
    "\n",
    "We can use the script `wikifil.pl` by [Matt Mahoney](http://mattmahoney.net/dc/textdata.html) to clean the data. This script filters Wikipedia XML dumps to \"clean\" text consisting only of lowercase letters (a-z, converted from A-Z), and spaces (never consecutive).  All other characters are converted to spaces.  Only text which normally appears in the web browser is displayed, tables are removed, image captions are preserved, links are converted to normal text and finally digits are spelled out. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 4 ms, sys: 64 ms, total: 68 ms\n",
      "Wall time: 1min 42s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "wiki_dataset_original = data_path + 'enwik9'\n",
    "wiki_dataset = data_path + 'text9'\n",
    "if not os.path.isfile(wiki_dataset):\n",
    "    os.system(\"perl wikifil.pl \" +  wiki_dataset_original + \" > \" + wiki_dataset)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's compute the Skipgram model of the dataset. We will obtain a vector of 50 dimensions for each word. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ 0.16798063  0.0098041  -0.03984898  1.04978788 -0.18959683  0.85292447\n",
      " -0.4180353  -0.40085188 -1.08187306  0.8872596   0.41947821  0.19981055\n",
      " -0.13649428 -0.24468173 -1.23089099 -0.1748575  -0.03422425  0.43458366\n",
      " -0.36701286  0.0804043   0.31023765  0.16417238  0.54866934 -0.75781947\n",
      "  0.2767764   0.33512017 -0.01796924 -0.17177744 -0.8444469   0.28967109\n",
      "  0.18059613 -0.70702893  0.42970082  0.05584288 -0.39817047  0.16206688\n",
      "  0.73674601 -0.08298894  0.16093367  0.36577964 -0.71077859 -0.04718487\n",
      " -0.22060201 -0.63146096 -0.58351386 -0.96920103  0.37770718 -0.17785279\n",
      "  0.2812494  -1.24845505]\n",
      "CPU times: user 13min 1s, sys: 8.76 s, total: 13min 10s\n",
      "Wall time: 3min 29s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "# Learn the word representation using skipgram model\n",
    "output_skipgram = data_path + 'skipgram'\n",
    "if os.path.isfile(output_skipgram + '.bin'):\n",
    "    skipgram = fasttext.load_model(output_skipgram + '.bin')\n",
    "else:\n",
    "    skipgram = fasttext.skipgram(wiki_dataset, output_skipgram, lr=0.02, dim=50, ws=5,\n",
    "        epoch=1, min_count=5, neg=5, loss='ns', bucket=2000000, minn=3, maxn=6,\n",
    "        thread=4, t=1e-4, lr_update_rate=100)\n",
    "print(np.asarray(skipgram['king']))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Since each word is represented in a multidimensional space, we can compute the similarity between words. In that space, the difference between king and queen will be smaller than between king and woman. Makes sense, right? At the same time, the difference between man and woman will be smaller than king and woman. \n",
    "\n",
    "With word representations, we are able to map from words to numerical values. What we are doing in fact is to featurize the words. The feautures can be used in different algorithms to learn the meaning of a text."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Number of words in the model:  218316\n",
      "12.5879589794\n",
      "20.1630534893\n",
      "11.2704082606\n"
     ]
    }
   ],
   "source": [
    "print(\"Number of words in the model: \", len(skipgram.words))\n",
    "\n",
    "# Get the vector of some word\n",
    "Droyals = np.sqrt(pow(np.asarray(skipgram['king']) - np.asarray(skipgram['queen']), 2)).sum()\n",
    "print(Droyals)\n",
    "Dpeople = np.sqrt(pow(np.asarray(skipgram['king']) - np.asarray(skipgram['woman']), 2)).sum()\n",
    "print(Dpeople)\n",
    "Dpeople2 = np.sqrt(pow(np.asarray(skipgram['man']) - np.asarray(skipgram['woman']), 2)).sum()\n",
    "print(Dpeople2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Word space visualization\n",
    "\n",
    "Once we have the vectorial word representation, where each word is located in a 50 dimensional space, it would be great if we could visualize the words in a reduced space.\n",
    "\n",
    "For that we will use t-Distributed Stochastic Neighbor Embedding (t-SNE) from [van der Mateen and Hinton (2008)](http://jmlr.org/papers/volume9/vandermaaten08a/vandermaaten08a.pdf). t-SNE is a dimensionality reduction method that is particularly well suited for the visualization of high-dimensional datasets. The idea is to compute the probability distribution of pairs of high dimensional objects in such a way that similar objects have high probability of being clustered together and disimilar objects have low probability of being clustered together. Afterwards, the algorithm projects these probabilities in the low dimensional space and optimizes the distance with respect to the object's location in that space. Therefore, at the end of the optimization, similar objects are close in the low dimensional space. \n",
    "\n",
    "First, we must define the words that we are going to analyze out of the complete corpus,  that contains more than 200,000 objects. We have chosen 10 words related to people and 10 words related to animals. \n",
    "\n",
    "Once we select the words to analize, we are going to get the word vectors of these words."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "218316\n"
     ]
    }
   ],
   "source": [
    "print(len(skipgram.words))\n",
    "targets = ['man','woman','king','queen','brother','sister','father','mother','grandfather','grandmother',\n",
    "           'cat','dog','bird','squirrel','horse','pig','dove','wolf','kitten','puppy']\n",
    "classes = [1,1,1,1,1,1,1,1,1,1,\n",
    "           2,2,2,2,2,2,2,2,2,2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "X_target=[]\n",
    "for w in targets:\n",
    "    X_target.append(skipgram[w])\n",
    "X_target = np.asarray(X_target)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The next step is to select a subset of the dataset, because computing more than 200.000 objects is too expensive."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "word_list = list(skipgram.words)[:10000]\n",
    "X_subset=[]\n",
    "for w in word_list:\n",
    "    X_subset.append(skipgram[w])\n",
    "X_subset = np.asarray(X_subset)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We add both datasets together."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(10020, 50)\n"
     ]
    }
   ],
   "source": [
    "X_target = np.concatenate((X_subset, X_target))\n",
    "print(X_target.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we compute the t-SNE algorithm."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[t-SNE] Computing pairwise distances...\n",
      "[t-SNE] Computed conditional probabilities for sample 1000 / 10020\n",
      "[t-SNE] Computed conditional probabilities for sample 2000 / 10020\n",
      "[t-SNE] Computed conditional probabilities for sample 3000 / 10020\n",
      "[t-SNE] Computed conditional probabilities for sample 4000 / 10020\n",
      "[t-SNE] Computed conditional probabilities for sample 5000 / 10020\n",
      "[t-SNE] Computed conditional probabilities for sample 6000 / 10020\n",
      "[t-SNE] Computed conditional probabilities for sample 7000 / 10020\n",
      "[t-SNE] Computed conditional probabilities for sample 8000 / 10020\n",
      "[t-SNE] Computed conditional probabilities for sample 9000 / 10020\n",
      "[t-SNE] Computed conditional probabilities for sample 10000 / 10020\n",
      "[t-SNE] Computed conditional probabilities for sample 10020 / 10020\n",
      "[t-SNE] Mean sigma: 0.478634\n",
      "[t-SNE] Iteration 25: error = 23.4018832, gradient norm = 0.0507323\n",
      "[t-SNE] Iteration 50: error = 23.6799925, gradient norm = 0.0496832\n",
      "[t-SNE] Iteration 75: error = 22.9733908, gradient norm = 0.0422842\n",
      "[t-SNE] Iteration 100: error = 23.3114248, gradient norm = 0.0461937\n",
      "[t-SNE] KL divergence after 100 iterations with early exaggeration: 23.311425\n",
      "[t-SNE] Iteration 125: error = 3.4371201, gradient norm = 0.0023642\n",
      "[t-SNE] Iteration 150: error = 3.3497492, gradient norm = 0.0015046\n",
      "[t-SNE] Iteration 175: error = 3.3312840, gradient norm = 0.0014499\n",
      "[t-SNE] Iteration 200: error = 3.3264371, gradient norm = 0.0014363\n",
      "[t-SNE] Error after 200 iterations: 23.311425\n",
      "(10020, 2)\n",
      "CPU times: user 34min 39s, sys: 1h 12min 6s, total: 1h 46min 46s\n",
      "Wall time: 21min 6s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "X_tsne = TSNE(n_components=2, perplexity=40, init='pca', method='exact',\n",
    "                  random_state=0, n_iter=200, verbose=2).fit_transform(X_target)\n",
    "print(X_tsne.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we draw all the words in the same plot, it would be impossible to understand anything, so we are only going to represent the target words."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(20, 2)\n"
     ]
    }
   ],
   "source": [
    "X_tsne_target = X_tsne[-20:,:]\n",
    "print(X_tsne_target.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def plot_words(X, labels, classes=None, xlimits=None, ylimits=None):\n",
    "    fig = figure(figsize=(6, 6))\n",
    "    if xlimits is not None:\n",
    "        xlim(xlimits)\n",
    "    if ylimits is not None:\n",
    "        ylim(ylimits)\n",
    "    scatter(X[:, 0], X[:, 1], c=classes)\n",
    "    for i, txt in enumerate(labels):\n",
    "        annotate(txt, (X[i, 0], X[i, 1]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAgMAAAH/CAYAAADKY96oAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAAPYQAAD2EBqD+naQAAIABJREFUeJzs3XmcTnXj//HXua7Z95UskyUTY22GlJ0UaRuqX5GlLKW7\nknuU6NuCSqo7JFruIksiZZcS2ck6Y0n31AyF6g5ZB2OZmevz+2PMdbsMMrgMzvv5eFyP23XO53zO\n5xzdzvs653M+H8sYg4iIiNiXo7gbICIiIsVLYUBERMTmFAZERERsTmFARETE5hQGREREbE5hQERE\nxOYUBkRERGxOYUBERMTmFAZERERsTmFARETE5rweBizLKm1Z1qeWZe22LCvbsqwNlmUleXu/IiIi\ncm58vFm5ZVkRwHJgPtAS2A3EA/u8uV8RERE5d5Y3JyqyLOsNoJ4xponXdiIiIiIXxNuPCe4G1lqW\n9YVlWTsty0qzLKubl/cpIiIiReDtOwNHAAMMBiYDdYFhQHdjzKenKR9N/uOErcBRrzVMRETk6hMA\nlAe+NcbsKcqG3g4Dx4DVxphGJy0bBtQxxjQ4TfmHgM+81iAREZGrX3tjzISibODVDoTAn0D6KcvS\ngXvPUH4rwPjx40lISPBis64+KSkpDB06tLibcUXROTs/Om9Fp3N2fnTeiiY9PZ0OHTrAiWtpUXg7\nDCwHKp+yrDKw7QzljwIkJCSQlKS3D4siPDxc56yIdM7Oj85b0emcnR+dt/NW5Mfs3u5AOBS42bKs\n5y3Luu7EY4BuwAgv71dERETOkVfDgDFmLdAGaAf8ALwA9DTGfO7N/YqIiMi58/ZjAowxXwNfe3s/\nIiIicn40N8FVol27dsXdhCuOztn50XkrOp2z86Pzdul49dXCojoxZ0FqamqqOo2IiIgUQVpaGrVr\n1waobYxJK8q2ujMgIiJicwoDIiIiNqcwICIiYnMKAyIiIjanMCAiImJzCgMiIiI2pzAgIiJicwoD\nIiIiNqcwICIiYnMKAyIiIjanMCAiImJzCgMiIiI2pzAgIiJicwoDIiIiNqcwICIiYnMKAyIiIjan\nMCAiImJzCgMiIiI2pzAgIiJicwoDIiIiNqcwICIiYnMKAyIiIjanMCAiImJzCgMiIiI2pzAgIiJi\ncwoDIiIiNqcwICIiYnMKAyIiIjanMCAiImJzCgMiIiI2pzAgIiJicwoDIiIiNqcwICIiYnMKAyIi\nclVq1qwZvXr1OuP6ChUq8O677xa53sWLF+NwOMjKyrqQ5l1WfIq7ASIiIsVh7dq1BAcHn9e2lmVd\n5NYUL4UBERGxpejo6LOuz83NxcfHHpdJPSYQEZGrVm5uLj169CAiIoLY2Fhefvll97pTHxM4HA4+\n/PBDkpOTCQkJ4fXXXwfg66+/pnLlygQFBdG8eXO2bt16qQ/D6xQGRETkqjVmzBh8fX1Zs2YN7777\nLkOGDGHUqFFnLD9gwADuvfdeNm3aRJcuXfj999+57777SE5OZsOGDXTr1o2+fftewiO4NOxx/0NE\nRGzp2muvZciQIQDEx8ezceNGhg4dSteuXU9bvn379jz88MPu7y+88AKVKlXirbfe8qij4PvVQncG\nRETkqmCMYerUqfy/+++nZcuWbN++ncTERI8y9erVIzMzE2PMaeuoXbu2x/f09HRuuummQnVcbXRn\nQERErnjGGLp168Ynn3xCWaeToLw8fgV27drFjh07uOaaa86pnvN9u+BKpzsDIiJyxVu0aBGffPIJ\n9wDd8vJ4CCgNZB86RL9+/dzlVqxYQXx8/Dm/GpiQkMDq1as9lq1YseLiNfwyoTAgIiJXvClTphDt\n48PJDwV8AQsYO2YMGRkZTJw4kREjRvDPf/7znOt9/PHHyczM5LnnniMjI4MJEyYwduzYi938Yqcw\nICIiV7zc3Fx8yL/4n+wawGUMdevWpUePHqSkpNCtWzeg8MBBp7tbEBcXx5QpU5gxYwY33HADH330\nEYMGDfLOQRQj60ydKIqDZVlJQGpqaipJSUnF3RwREblCzJw5k+TkZB4Crj+xLBv42Onk9gceYMKE\nCcXYuksjLS2toANkbWNMWlG2VQdCERG54t15553c3qIFk+bNo7IxBAM/OZ34hoUxYMCA4m7eZU+P\nCURE5IrndDqZMWsWbw8ZQmBiIgcqVaJj9+6kpqURHx9f3M277OnOgIiIXBX8/Pz45z//WaQOgpJP\ndwZERERsTmFARETE5hQGREREbE5hQERExOYUBkRExPaaNWtGr169irsZxUZhQERExOYUBkRERGxO\nYUBERGwlOzubTp06ERoaSpkyZRgyZIjH+v3799OpUyeioqIIDg7mjjvuYPPmzQAcPHiQoKAgvv32\nW49tpk2bRlhYGEePHgXg999/58EHHyQyMpLo6Ghat27Ntm3bLs0BngeFARERsZVnn32WpUuXMmvW\nLObOncuiRYtIS/vfUP4PP/wwaWlpfPXVV6xcuRJjDHfccQd5eXmEhoZy1113FZrrYMKECbRp04aA\ngAByc3Np2bIl4eHhLF++nO+//57Q0FBuv/12cnNzL/XhnhtjzGXzAZIAk5qaakRERC62Q4cOGX9/\nfzNlyhT3sr1795qgoCCTkpJiMjMzjWVZZuXKle71e/bsMUFBQWby5MnGGGOmT59uwsLCzJEjR4wx\nxmRlZZnAwEAzd+5cY4wxn376qUlISPDY77Fjx0xQUJCZN2+e144tNTXVAAZIMkW8/mo4YhERuWrl\n5OQwefJkZsyYgWVZ3HDDDeTk5FC3bl13mcjISCpXrgxAeno6vr6+HuujoqKoXLky6enpANxxxx34\n+Pgwc+ZMHnjgASZPnkx4eDjNmzcHYOPGjWRmZhIaGurRlmPHjrFlyxZuvfVWbx92kSkMiIjIVen4\n8ePcdccdzJs/nzinE2MMn3/+uXvd+fL19eX+++9nwoQJPPDAA0ycOJEHH3wQhyP/yfuhQ4eoU6cO\nEyZMKLjr7RYbG3v+B+RF6jMgIiJXpZEjRzJ/wQI6Al3z8ujmctH2xLo333zTXW7fvn1kZGQAkJCQ\nQE5ODqtWrXKv37NnDz///DNVq1Z1L2vfvj1z5szhP//5DwsWLKBDhw7udUlJSWRmZhIbG0vFihU9\nPqfeLbhcKAyIiMhV6YvPP6cScN1Jy6oA4cC4ceNYuHAhmzZtonPnzjidTgAqVapEcnIyjz76KMuX\nL2fDhg106NCBuLg4kpOT3fU0btyYkiVL0r59eypWrEidOnXc69q3b09MTAzJycksW7aMrVu3smjR\nInr27Ml///vfS3LsRaUwICIiV6UjR47gd8pteoBSQGhICPfccw8tWrSgUaNG1K5d271+9OjR1K5d\nm7vvvpsGDRrgcDiYPXu2OzAUaNeuHRs3bvS4KwAQGBjIkiVLuPbaa7nvvvuoWrUqjz76KMeOHSMs\nLMwrx3qhrFOfZxQny7KSgNTU1FSSkpKKuzkiInIF69evH28OHMjjeXlEnli2F/jQ4eDF/v156aWX\nirN5F11aWlpBqKltjEn7u/Inu2R3BizL6mtZlsuyrCF/X1pEROTC9OjRg9Jly/Kx08nXwGxgpNNJ\n2Wuv5cknnyzu5l1WLkkYsCzrRuAxYMOl2J+IiEhMTAwrVq2i+9NPsysujr/i4uj+9NOsXL2aqKio\n4m7eZcXrrxZalhUCjAe6AVfXPRkREbmslSxZkiFDhhQaclg8XYo7A+8Bs4wxCy7BvkRERKSIvHpn\nwLKstsANQJ2/KysiIiLFw2thwLKsssA7wK3GmBxv7UdEREQujDfvDNQGYoE0y7KsE8ucQGPLsp4C\n/M0Z3mtMSUkhPDzcY1m7du1o166dF5srIiJyZZg4cSITJ070WHbgwIHzrs9r4wxYlhUMlDtl8Rgg\nHXjDGJN+mm00zoCIiMh5uJBxBrx2Z8AYcxj4z8nLLMs6DOw5XRAQERGR4nGphyO+fIY7FBEREeAS\nT2FsjLnlUu5PRERE/p4mKhIREbE5hQERERGbUxgQERGxOYUBERERm1MYEBERsTmFAREREZtTGBAR\nEbE5hQERERGbUxgQERGxOYUBERERm1MYEBERsTmFAREREZtTGBAREbE5hQERERGbUxgQERGxOYUB\nERERm1MYEBERsTmFAREREZtTGBAREbE5hQERERGbUxgQERGxOYUBERERm1MYEBERsTmFAREREZtT\nGBAREbE5hQERERGbUxgQERGxOYUBERERm1MYEBERsTmFAREREZtTGBAREbE5hQERERGbUxgQERGx\nOYUBERERm1MYEBERsTmFAREREZtTGBAREbE5hQERERGbUxgQERGxOYUBERERm1MYEBERsTmFARER\nEZtTGBAREbE5hQERERGbUxgQERGxOYUBERERm1MYEBERsTmFAREREZtTGBAREbE5hQERERGbUxgQ\nERGxOYUBERERm1MYEBERsTmFAREREZtTGBAREbE5hQERERGbUxgQERGxOYUBERERm1MYEBERsTmF\nAREREZtTGBAREbE5hQERERGbUxgQERGxOYUBERERm1MYEBERsTmFAREREZtTGBAREbE5r4YBy7Ke\ntyxrtWVZWZZl7bQsa5plWdd7c58iIiJSNN6+M9AIGA7cBNwK+AJzLcsK9PJ+RURE5Bz5eLNyY8wd\nJ3+3LOsRYBdQG1jmzX2LiIjIubnUfQYiAAPsvcT7FRERkTO4ZGHAsiwLeAdYZoz5z6Xar4iIiJyd\nVx8TnOJ9oCrQ4O8KpqSkEB4e7rGsXbt2tGvXzktNExERuXJMnDiRiRMneiw7cODAeddnGWMutE1/\nvxPLGgHcDTQyxmw/S7kkIDU1NZWkpCSvt0tERORqkZaWRu3atQFqG2PSirKt1+8MnAgCyUCTswUB\nERERKR5eDQOWZb0PtAPuAQ5bllXyxKoDxpij3ty3iIiInBtvdyB8HAgDFgH/PenzgJf3KyIiIufI\n2+MMaLhjERGRy5wu1iIiIjanMCAiImJzCgMiIiI2pzAgIiJicwoDIiIiNqcwICIiYnMKAyIiIjan\nMCAiImJzCgMiIiI2pzAgIiJicwoDIiIiNqcwICIiYnMKAyIiIjanMCAiImJzCgMiIiI2pzAgIiJi\ncwoDIiIiNqcwICIiYnMKAyIiIjanMCAiImJzCgMiIiI2pzAgIiJicwoDIiIiNqcwICIiYnMKAyIi\nIjanMCAiImJzCgMiIiI2pzAgIiJicwoDIiIiNqcwICIiYnMKAyIiIjanMCAiImJzCgMiIiI2pzAg\nIiJicwoDIiIiNqcwICIiYnMKAyIiIjanMCAiImJzCgMiIiI2pzAgIiJicwoDIiIiNqcwICIiYnMK\nAyIiIjanMCAiImJzCgMiIiI2pzAgIiJicwoDIiIiNqcwICIiYnMKAyIiIjanMCAiImJzCgMiIiI2\npzAgIiJicwoDIiIiNqcwICIiYnMKAyIiIjanMCAiImJzCgMiIiI2pzAgIiJicwoDIiIiNqcwICIi\nYnMKAyIiIjanMCAiImJzCgMiIiI2pzAgIiJicwoDIiIiNqcwICIiYnNeDwOWZT1pWdavlmUdsSxr\npWVZN3p7nyIiInLuvBoGLMt6EBgM9AMSgQ3At5ZlxXhzvyIiInLuvH1nIAX4tzFmnDHmJ+BxIBvo\n4uX9ioiIyDnyWhiwLMsXqA3ML1hmjDHAd0A9b+1XREREisabdwZiACew85TlO4FrvLhfERERKQK9\nTSAiImJzPl6sezeQB5Q8ZXlJYMfZNkxJSSE8PNxjWbt27WjXrt1FbaCIiMiVaOLEiUycONFj2YED\nB867Piv/Mb53WJa1ElhljOl54rsFbAfeNcb86zTlk4DU1NRUkpKSvNYuERGRq01aWhq1a9cGqG2M\nSSvKtt68MwAwBBhjWVYqsJr8twuCgDFe3q+IiIicI6+GAWPMFyfGFHiF/McD64GWxpi/vLlfERER\nOXfevjOAMeZ94H1v70dERETOj94mEBERsTmFAREREZtTGBAREbE5hQERERGbUxgQERGxOYUBERER\nm1MYEBERsTmFAREREZtTGBAREbE5hQERERGbUxgQERGxOYUBERERm1MYEBERsTmFAREREZtTGBAR\nEbE5hQERERGbUxgQERGxOYUBERERm1MYEBERsTmFAREREZtTGBAREbE5hQERERGbUxgQERGxOYUB\nERERm1MYEBERsTmFAREREZtTGBAREbE5hQERERGbUxgQERGxOYUBERERm1MYEBERsTmFAREREZtT\nGBAREbE5hQERERGbUxgQERGxOYUBERERm1MYEBERsTmFAREREZtTGBAREbE5hQERERGbUxgQERGx\nOYUBERERm1MYEBERsTmFAREREZtTGBAREbE5hQERERGbUxgQERGxOYUBERERm1MYEBERsTmFARER\nEZtTGBAREbE5hQERERGbUxgQERGxOYUBERERm1MYEBERsTmFAREREZtTGBAREbE5hQFxa9asGb16\n9SruZoiIyCWmMCAiImJzCgPiNTk5OcXdBBEROQcKA+LB5XLRp08foqOjKVWqFAMGDHCv++2330hO\nTiY0NJTw8HAefPBBdu3a5V4/YMAAEhMTGTVqFBUrViQwMBCAyZMnU7NmTYKCgoiJiaFFixYcOXLE\nvd3IkSOpWrUqgYGBVK1alQ8++ODSHbCIiOBT3A2Qy8vYsWPp1asXq1ev5vvvv+eRRx6hYcOG3HLL\nLdxzzz2EhYWxdOlScnJyeOKJJ2jbti0LFixwb79582amTp3KtGnTcDqd7Nixg4ceeoi3336b1q1b\nc/DgQZYuXYoxBoDPPvuM/v37895773HDDTewbt06Hn30UUJCQujYsWNxnQYREVtRGBAPNWvW5KWX\nXgLguuuuY8SIEcyfPx9jDD/++CNbt26ldOnSAIwbN45q1aqRmppK7dq1gfxHA59++ilRUVEArFu3\njry8PNq0aUNcXBwA1apVc++vf//+DB48mOTkZADKlSvHjz/+yIcffqgwICJyiSgM2NyOHTuYMWMG\nx44dIzs7m7p163qsL1WqFLt27SI9PZ24uDh3EABISEggIiKC9PR0dxgoV66cOwgA1KpVi+bNm1O9\nenVatmxJixYtuP/++4mIiCA7O5stW7bQtWtXunXr5t4mLy+PiIgILx+5iIgUUBiwsQ8++ICeTz9N\nXl4eTssix+Xi6NGjuFwuHI787iSWZeFyuc65zuDgYI/vDoeDuXPnsmLFCubOncvw4cN54YUXWL16\ntbtPwciRIwuFEKfTeYFHJyIi50odCG1q7dq1PPHEE9TKzaW3MfRxuYgCNm7cyMiRIwuVT0hIYPv2\n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\nZa7qYSAiRkTE1ojoiYgZR28hSZIG02CcGVgK7KC4EIIkSaozVQ0DEXEFcBmwmAOvcSlJkupE1SYQ\nRsQpwI+AecDeo7xckiTVSDXPDLQCK1NKv6zie0iSpE+pojAQEXeUJwIebtsXEVMiYhHFbbC+t7/p\nZ165JEn6TFQ6TPB9im/8R/I68GVgFvDbg24Y8YuIeDildP2RdnDLLbcwZsyYA55bsGABCxYsqLBc\nSZKOPatXr2b16tUHPNfZ2Tng/VXlRkURMYHiotb7nQqsA64GtqSUdh6mnTcqkiRpAD7NjYqqMoEw\npbSj7+OI6KYYKth+uCAgSZJqYzCvQOh1BiRJqkODcm+ClNKbQMNgvJckSaqM9yaQJClzhgFJkjJn\nGJAkKXOGAUmSMmcYkCQpc4YBSZIyZxiQJClzhgFJkjJnGJAkKXOGAUmSMmcYkCQpc4YBSZIyZxiQ\nJClzhgFJkjJnGJAkKXOGAUmSMmcYkCQpc4YBSZIyZxiQJClzhgFJkjJnGJAkKXOGAUmSMmcYkCQp\nc4YBSZIyZxiQJClzhgFJkjJnGJAkKXOGAUmSMmcYkCQpc4YBSZIyZxiQJClzhgFJkjJnGJAkKXOG\nAUmSMmcYkCQpc4YBSZIyZxiQJClzhgFJkjJnGJAkKXOGAUmSMmcYkCQpc4YBSZIyZxiQJClzhgFJ\nkjJnGJAkKXOGAUmSMmcYkCQpc4YBSZIyZxiQJClzhgFJkjJnGJAkKXOGAUmSMmcYkCQpc4YBSZIy\nZxiQJClzhgFJkjJnGJAkKXOGAUmSMmcYkCQpc4YBSZIyZxiQJClzhgFJkjJnGJAkKXOGAUmSMmcY\nkCQpc4YBSZIyZxg4RqxevbrWJQw59tnA2G+Vs88Gxn4bPFUNAxExNyI2RcQHEfFuRPxbNd8vZ/7R\nVM4+Gxj7rXL22cDYb4NnWLV2HBFXAz8C/hZ4FhgOnFut95MkSQNTlTAQEQ3AcuBvUkptfX70P9V4\nP0mSNHDVGiaYCZwKEBHtEbEzItZExLQqvZ8kSRqgag0TTAIC+AfgFuBNYDGwPiLOSintOUy74wC2\nbdtWpbKOXZ2dnbS3t9e6jCHFPhsY+61y9tnA2G+V6XPsPK7iximlfm/AHUDPEbZ9wBRgQfnxX/Zp\nOwL4DfCNI+x/IZDc3Nzc3NzcBrwtrOTYnlKq+MzA94HWo7xmO+UhAqA3pqSUfhcR24HTj9B2HfDn\nwBvAhxXWJklSzo4DJlIcSytSURhIKXUAHUd7XUS8BPwWmAq8UH5ueLnIN4+y/0cqqUmSJPV6YSCN\nqjJnIKX0fxHxQ+AfI2IHRQD4NsXpi8er8Z6SJGlgqnadAYoJgx8BDwLHA5uBS1JKnVV8T0mSVKEo\nT9yTJEmZ8t4EkiRlzjAgSVLm6j4MRMSIiNgaET0RMaPW9dS7iHgiIt6MiL3lKz8+GBGfr3Vd9Soi\nWiLixxGxvXxDrVcj4jvl1S86goj4+4j4eUR0R8S7ta6nHkXEX0XE6+W/x00R8YVa11TvIuKiiHgy\nIv63/Lk/r9Y11buI+LuI2BIR70fE7oj4aURMqWQfdR8GgKXADoqVCDq6Z4E/obj401eBybiC40jO\nprha5jeAcyiumPlN4Lu1LGqIGA48BtxT60LqUURcAyyjuBLr+cB/AesiYmxNC6t/o4CtwLfwc7+/\nLgLuBv4IuJTib/PfI+L4/u6gricQRsQVFBc6uhr4FXBeSum/a1vV0BIRVwI/BUamlPbVup6hICIW\nA99MKZ1Z61qGgoj4OnBnSqm51rXUk4jYBGxOKf11+XEAvwZ+kFJaWtPihoiI6AG+klJ6sta1DCXl\nwPkb4OKU0vP9aVO3ZwYi4hSKWyB/Ddhb43KGpIhoprii488NAhVpAjztrQErDzNdADyz/7lUfPP6\nD2BWrepSNpoozqr0+3OsbsMAxWWPV6aUflnrQoaaiPjniOgC3gFOA75S45KGjIg4E7gJ+GGta9GQ\nNhZoAHYf9Pxu4HODX45yUT4DtRx4PqX0q/62G9QwEBF3lCeEHG7bFxFTImIR0Ah8b3/Twayz3vS3\n3/o0WQqcB1xGcfOoh2pSeA0NoM+IiPHAWuDRlNIDtam8tgbSb5LqykqK+U9/VkmjQZ0zEBEnAScd\n5WWvU0xK+uODnm8APgYeTildX4Xy6lY/+217SunjQ7QdTzFOOSultLka9dWjSvssIk4FngNeyO33\nq6+B/K45Z+CTysMEHwBX9x3vjog2YExK6apa1TaUOGegMhGxArgSuCil9FYlbat5OeJPqOBGRzcD\nt/V56lSKuzD9KbClOtXVr/7222E0lP8d+RmVMyRU0mflwPQs8J/AX1Szrnr3KX/XVJZS+qh8w7Y5\nwJPQe/p2DvCDWtamY1M5CMwHvlRpEIBBDgP9lVLa0fdxRHRTDBVsTyntrE1V9S8i/hD4AvA88B5w\nJvBPwKvAizUsrW6Vzwispzgj9W1gXPGZDSmlg8d71UdEnAY0Ay1AQ0T8fvlHr6WUumtXWd34F6Ct\nHAq2UCxbPQFoq2VR9S4iRlF8du0fHp5U/t16N6X069pVVr8iYiWwAJgHdJcn4AN0ppQ+7Nc+6nlp\n4X4R0QJsB853aeHhRcS5wF3ADIq1ursoxsC/m1LaVcva6lX5FPfB8wOCYvJ3wyGaqCwiWoFrD/Gj\nL6eUNgx2PfUoIr5FETJPoVg7f3NK6Re1raq+RcSXKIbsDj44rUopZX3m7nDKwymHOphfn1J6sF/7\nGAphQJIkVU89Ly2UJEmDwDAgSVLmDAOSJGXOMCBJUuYMA5IkZc4wIElS5gwDkiRlzjAgSVLmDAOS\nJGXOMCBJUuYMA5IkZe7/Aegna2GhgSVdAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fa8fe7d6208>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_words(X_tsne_target, targets, classes=classes)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the previous plot we can see the target words represented in a 2 dimensional space. As you can see, words related to animals are clustered together. The words related to people form a couple of clusters. We can take a zoom to the cluster to the bottom right to see what words are clustered together"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\nFhZy5pln5to899xzxBiZPn067733Hg8//HBu2fTp03nrrbeYMWMGEydOZMKECUyYMCG3/Hvf+x7z\n5s3joYce4tVXX+Xss89m6NChLF68OFdn3bp1/PSnP+Xuu+/mb3/7G+3bt6+TfwtJuw9PjUmqEy1b\ntqRly5YUFhay3377AVQKPQDjx4+nffv2vP766/Tq1StXr23btluElLZt2/KLX/yCEALdu3fn1FNP\n5a9//Svnn38+S5cuZcKECSxbtowDDjgAgMsuu4zHH3+ce+65h7FjxwKwYcMGfv3rX291rpKkxssg\nJKnWlZeXs3jxYv75z39WKl+0aBHXX3898+bNY9WqVbl5OkuXLqVXr1419nn44YdXms/ToUMHXnvt\nNQBee+01Kioq6N69OzHGXJ3PPvuMfffdN/e5SZMmhiApcQYhSbVm9erVjBgxkmnTpuTKmjVrxgcf\nfECbNm047bTT6NKlC+PHj6djx45s3LiRww8/nM8++2yrfe+5556VPocQ2LhxIwBr165ljz32YP78\n+RQUVJ4B0KJFi0pjkZQ2g5CkWjNixEiefHIuMAkYBPw369ffz/Dh3+D++39PeXk5d999NwMGDABg\n9uzZldo3adIEoNIk6G3Rt29fKioqWLFiRa5vSaqKQUhSrSgvL88eCZoEnJst/QIwjWnTprBq1Sra\ntWvH7373Ow444ACWLFnC1VdfXel0V/v27WnWrBlTp07lwAMPpGnTprRq1Wqr311UVMSIESM477zz\nuPXWW+nbty8rV65k+vTpHHXUUQwdOrQ2VllSA+RVY5Jqxb+vzhq02ZKmueUPPvggZWVl9O7dm8sv\nv5xbb721Us3CwkLuuusufvvb33LggQdyxhlnAJkbM27tJosTJkzgvPPO44orruCwww5j2LBhvPDC\nCxx88ME7tV6bLruX1DiE/ImE2rVCCMVAWVlZGcXFxfU9HKlOlZeX06NHDyofESL7eSTl5eUUFRXt\nUN/HH388ffv25fbbb98FI61ely5dKCkp4ZJLLsmV3XvvvZSUlLB69epa/W4pdfPnz6dfv34A/WKM\n82vrezwiJGmX+/zzz+nevTtDhpxCYeElZMLPMmAShYWXMmTIKTscghqrDRs21PcQpCQZhCRt1dq1\nazn33HNp0aIFBx10EHfddRfHH388l112GZA5cjJ27FhGjRpF69atufDCCwE47LAi9trrU2AkcDAw\n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\ng5AkSUqWQUiSJCXLICRJkpJlEJIkSckyCEmSpGQZhCRJUrIMQpIkKVkGIUmSlCyDkCRJSpZBSJIk\nJcsgJEmSkmUQkiRJyTIISZKkZBmEJElSsgxCkiQpWQYhSZKULIOQJElKlkFIkiQlyyAkSZKSZRCS\nJEnJMghJkqRkGYQkSVKyDEKSJClZBiFJkpQsg5AkSUqWQUiSJCXLICRJkpJlEJIkSckyCEmSpGQZ\nhCRJUrIMQpIkKVkGIUmSlCyDkCRJSpZBSJIkJcsgJEmSkmUQkiRJyTIISZKkZBmEJElSsgxCkiQp\nWQYhSZKULIOQJElKlkFIkiQlyyAkSZKSZRCSJEnJMghJkqRkGYQkSVKyDEKSJClZBiFJkpQsg5Ak\nSUqWQUiSJCXLICRJkpJlEJIkSckyCEmSpGQZhCRJUrIMQpIkKVkGIUmSlCyDkCRJSpZBSJIkJcsg\nJEmSkmUQkiRJyTIISZKkZBmEJElSsgxCkiQpWQYhSZKULIOQJElKlkFIkiQlyyAkSZKSZRCSJEnJ\nMghJkqRkGYQkSVKyDEKSJClZBiFJkpSs/x/leOeNfJZU4QAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fa8dc20eb38>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_words(X_tsne_target, targets, xlimits=[0.5,0.7], ylimits=[-3.7,-3.6])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Conclusion \n",
    "In this notebook we have shown how to classify text, perform sentiment analysis, create word representations and visualize these representations in a XY plot. fastText is an easy to use framework to work with these NLP problems. Its most remarkable feature is its computation speed, that allows to train models very quick mantaining a high level of accuracy in comparison to other methods like [deep learning](https://arxiv.org/abs/1509.01626). \n"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python [default]",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
